<html>

<head>
<title>Video Riot by Scott Draves</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">

<style type="text/css">
video {
  display: none;
}
img {
  display: none;
}
body {
  margin: 0;
  scrollbars: none;
  background: #444;
  color: #ddd;
  font-family: Arial;
  font-size: 14px;
}
a:visited,a:link {
  color: #ddd;
}

</style>
<script src="webgl-debug.js"></script>
<script src="DAT.GUI.js"></script>
<script src="presets.js"></script>
<script type="text/javascript" src="glMatrix-0.9.5.min.js"></script>
<script type="text/javascript" src="webgl-utils.js"></script>

<script id="feedback-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;
    uniform bool uInvert;
    uniform float uBrightness;
    uniform float uContrast;
    uniform float uSaturation;

    // http://mouaif.wordpress.com/2009/01/05/photoshop-math-with-glsl-shaders/
vec3 ContrastSaturationBrightness(vec3 color, float brt, float sat, float con)
{
	// Increase or decrease theese values to adjust r, g and b color channels seperately
	const float AvgLumR = 0.5;
	const float AvgLumG = 0.5;
	const float AvgLumB = 0.5;
	
	const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
	
	vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
	vec3 brtColor = color * brt;
	vec3 intensity = vec3(dot(brtColor, LumCoeff));
	vec3 satColor = mix(intensity, brtColor, sat);
	vec3 conColor = mix(AvgLumin, satColor, con);
	return conColor;
}


    void main(void) {
        vec2 st = vTextureCoord;
        float upper = 1.0;
        float lower = 0.0;
        if (st.s > upper) st.s = 2.0*upper - st.s;
        if (st.t > upper) st.t = 2.0*upper - st.t;
	if (st.s <= lower) st.s = lower - st.s;
	if (st.t <= lower) st.t = lower - st.t;

        vec4 color = texture2D(uSampler, st);
        vec3 newcolor = ContrastSaturationBrightness(color.rgb,
				   uBrightness, uSaturation, uContrast);
        color.rgb = newcolor;

        if (uInvert)
            gl_FragColor = vec4(1.0-color.r, 1.0-color.g, 1.0-color.b, 1.0);
        else
            gl_FragColor = vec4(color.rgb, 1.0);
    }
</script>

<script id="copying-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;
    void main(void) {
        vec4 color = texture2D(uSampler, vTextureCoord);
        gl_FragColor = color;
    }
</script>

<script id="mixing-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision highp float;
    #endif

    varying vec2 vTextureCoord;
    uniform sampler2D uSampler;
    uniform sampler2D uSampler2;

    uniform float uLumaBar;
    uniform bool uLumaBW;
    uniform bool uColorize;

    void main(void) {
        vec4 color = texture2D(uSampler, vTextureCoord);

        vec4 final_color;
        float luma = (color.r + color.g + color.b)/3.0;
        float alpha;
        float luma3 = uLumaBar;
 
        vec2 lut;
        lut.s = luma;
        lut.t = luma;
        vec4 colorized;
        if (uColorize)
           colorized = texture2D(uSampler2, lut);
        else
           colorized = color;

	if (uLumaBW)
           alpha = (luma > luma3) ? 1.0 : 0.0;
        else
           alpha = (luma > luma3) ? 0.0 : 1.0;

        gl_FragColor = vec4(colorized.rgb, alpha);
    }
</script>

<script id="per-fragment-lighting-vs" type="x-shader/x-vertex">
    attribute vec3 aVertexPosition;
    attribute vec2 aTextureCoord;
    uniform mat4 uMVMatrix;
    varying vec2 vTextureCoord;

    void main(void) {
        gl_Position = uMVMatrix * vec4(aVertexPosition, 1.0);
        vTextureCoord = aTextureCoord;
    }
</script>


<script type="text/javascript">

    var gl;

    function initGL(canvas) {

        function throwOnGLError(err, funcName, args) {
           throw WebGLDebugUtils.glEnumToString(err) + " was caused by call to" + funcName;
        };
 
        try {
             gl = canvas.getContext("experimental-webgl");
            // gl = WebGLDebugUtils.makeDebugContext(canvas.getContext("experimental-webgl"), throwOnGLError);
            // gl = WebGLDebugUtils.makeDebugContext(canvas.getContext("experimental-webgl"), null);
            gl.viewportWidth = canvas.width;
            gl.viewportHeight = canvas.height;
        } catch (e) {
        }
        if (!gl) {
            alert("Could not initialise WebGL, sorry :-(");
        }
    }


    function getShader(gl, id) {
        var shaderScript = document.getElementById(id);
        if (!shaderScript) {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k) {
            if (k.nodeType == 3) {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        var shader;
        if (shaderScript.type == "x-shader/x-fragment") {
            shader = gl.createShader(gl.FRAGMENT_SHADER);
        } else if (shaderScript.type == "x-shader/x-vertex") {
            shader = gl.createShader(gl.VERTEX_SHADER);
        } else {
            return null;
        }

        gl.shaderSource(shader, str);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }

        return shader;
    }


    var feedbackShaderProgram;
    var mixingShaderProgram;
    var currentShaderProgram;

    function makeShaderProgram(vertex, fragment) {
        program = gl.createProgram();
        gl.attachShader(program, vertex);
        gl.attachShader(program, fragment);
        gl.linkProgram(program);

        if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
            alert("Could not initialise shader program");
        }


        program.vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition");
        gl.enableVertexAttribArray(program.vertexPositionAttribute);

        program.textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
        gl.enableVertexAttribArray(program.textureCoordAttribute);

        program.mvMatrixUniform = gl.getUniformLocation(program, "uMVMatrix");

        program.samplerUniform = gl.getUniformLocation(program, "uSampler");
        program.sampler2Uniform = gl.getUniformLocation(program, "uSampler2");
        program.invertUniform = gl.getUniformLocation(program, "uInvert");

        program.lumabarUniform = gl.getUniformLocation(program, "uLumaBar");
        program.lumabwUniform = gl.getUniformLocation(program, "uLumaBW");
        program.colorizeUniform = gl.getUniformLocation(program, "uColorize");

        program.brightnessUniform = gl.getUniformLocation(program, "uBrightness");
        program.contrastUniform = gl.getUniformLocation(program, "uContrast");
        program.saturationUniform = gl.getUniformLocation(program, "uSaturation");

   
        return program;
    }

    function initShaders() {
        var feedbackShader = getShader(gl, "feedback-fs");
        var mixingShader = getShader(gl, "mixing-fs");
        var copyingShader = getShader(gl, "copying-fs");

        var vertexShader = getShader(gl, "per-fragment-lighting-vs");
        feedbackShaderProgram = makeShaderProgram(vertexShader, feedbackShader);
        mixingShaderProgram = makeShaderProgram(vertexShader, mixingShader);
        copyingShaderProgram = makeShaderProgram(vertexShader, copyingShader);

        currentShaderProgram = mixingShaderProgram;
        gl.useProgram(currentShaderProgram);

    }


    function makeTexture() {
        var texture = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        // can only do this if POT
        // gl.generateMipmap(gl.TEXTURE_2D);
        gl.bindTexture(gl.TEXTURE_2D, null);
        return texture;
    }

    var videoTexture;


    function initTextureMaps() {
        videoTexture = makeTexture();
        colorizeTexture = makeTexture();
    }

    var mvMatrix = mat4.create();
    var mvMatrixStack = [];


    function mvPushMatrix() {
        var copy = mat4.create();
        mat4.set(mvMatrix, copy);
        mvMatrixStack.push(copy);
    }

    function mvPopMatrix() {
        if (mvMatrixStack.length == 0) {
            throw "Invalid popMatrix!";
        }
        mvMatrix = mvMatrixStack.pop();
    }

    function setMatrixUniforms() {
        gl.uniformMatrix4fv(currentShaderProgram.mvMatrixUniform, false, mvMatrix);
    }

    function degToRad(degrees) {
        return degrees * Math.PI / 180;
    }


    var rttFramebuffer0;
    var rttTexture0;

    var rttFramebuffer1;
    var rttTexture1;

    function createTexture() {
        var tex = gl.createTexture();
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
        // gl.generateMipmap(gl.TEXTURE_2D);
        return tex;
    }

    function createFramebuffer(tex) {
        var fb = gl.createFramebuffer();
        gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
        fb.width = 1024;
        fb.height = 512;

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, fb.width, fb.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);

        var renderbuffer = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer);
        gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, fb.width, fb.height);

        gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
        gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, renderbuffer);

        return fb;
    }

    var cubeVertexPositionBuffer;
    var cubeVertexTextureCoordBuffer;
    var cubeVertexIndexBuffer;

    var biunitSquareVertexPositionBuffer;
    var biunitSquareVertexTextureCoordBuffer;

    function initBuffers() {
       
        cubeVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
        vertices = [
            // Front face
            -3.0, -3.0,  0.0,
             3.0, -3.0,  0.0,
             3.0,  3.0,  0.0,
            -3.0,  3.0,  0.0,

        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        cubeVertexPositionBuffer.itemSize = 3;
        cubeVertexPositionBuffer.numItems = 4;

        cubeVertexTextureCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
        var textureCoords = [
            -1.0, -1.0,
            2.0, -1.0,
            2.0, 2.0,
            -1.0, 2.0,
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
        cubeVertexTextureCoordBuffer.itemSize = 2;
        cubeVertexTextureCoordBuffer.numItems = 4;

        cubeVertexIndexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
        var cubeVertexIndices = [
            0, 1, 2,      0, 2, 3,    // Front face
        ];
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
        cubeVertexIndexBuffer.itemSize = 1;
        cubeVertexIndexBuffer.numItems = 6;





        biunitSquareVertexPositionBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexPositionBuffer);
        vertices = [
             1, 1, 0,
            -1, 1, 0,
             1, -1, 0,
            -1, -1, 0,
            ];

        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
        biunitSquareVertexPositionBuffer.itemSize = 3;
        biunitSquareVertexPositionBuffer.numItems = 4;

        biunitSquareVertexTextureCoordBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexTextureCoordBuffer);
        var textureCoords = [
            1.0, 1.0,
            0.0, 1.0,
            1.0, 0.05, // on my iMac i get a green bar at bottom if this is 0
            0.0, 0.05,
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
        biunitSquareVertexTextureCoordBuffer.itemSize = 2;
        biunitSquareVertexTextureCoordBuffer.numItems = 4;
    }

    var textureAngle = 0;

    function drawSceneRecursive(fb, tex0, tex1) {
        gl.viewport(0, 0, fb.width, fb.height);
        gl.clear(gl.COLOR_BUFFER_BIT);


        // feedback layer ---------

        currentShaderProgram = feedbackShaderProgram;
        gl.useProgram(currentShaderProgram);

	gl.uniform1i(currentShaderProgram.invertUniform, mixer.invert);
	gl.uniform1f(currentShaderProgram.brightnessUniform, mixer.brightness);
	gl.uniform1f(currentShaderProgram.contrastUniform, mixer.contrast);
	gl.uniform1f(currentShaderProgram.saturationUniform, mixer.saturation);

        mat4.identity(mvMatrix);

        gl.disable(gl.DEPTH_TEST);

        mvPushMatrix();
        var rads = degToRad(textureAngle);
          mat4.scale(mvMatrix, [mixer.scale + 0.1 * Math.sin(1.1 * rads),
                                mixer.scale + 0.1 * Math.sin(1.21 * rads), 1.0]);

          mat4.rotate(mvMatrix, Math.sin(0.39 * rads), [0, 0, 1]);
          mat4.translate(mvMatrix, [0.2 * Math.sin(1.31 * rads),
                                    0.2 * Math.sin(1.44 * rads), 0.0]);
        


        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexPositionBuffer);
        gl.vertexAttribPointer(currentShaderProgram.vertexPositionAttribute, cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, cubeVertexTextureCoordBuffer);
        gl.vertexAttribPointer(currentShaderProgram.textureCoordAttribute, cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, tex1);
        gl.uniform1i(currentShaderProgram.samplerUniform, 0);


        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVertexIndexBuffer);
        setMatrixUniforms();
        gl.drawElements(gl.TRIANGLES, cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
        mvPopMatrix();


        // source layer --------------


        currentShaderProgram = mixingShaderProgram;
        gl.useProgram(currentShaderProgram);

        gl.enable(gl.BLEND);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

	gl.uniform1f(currentShaderProgram.lumabarUniform, mixer.lumakey);
	gl.uniform1i(currentShaderProgram.lumabwUniform, mixer.luma_bw);
	gl.uniform1i(currentShaderProgram.colorizeUniform, mixer.colorize != "");

        mvPushMatrix();

        if (mixer.jumpyBar) {
          mat4.translate(mvMatrix, [Math.random()-0.5, Math.random()-0.5, 0.0]);
          mat4.rotate(mvMatrix, Math.random()*Math.PI*2, [0, 0, 1]);
          mat4.scale(mvMatrix, [0.05, 1.0, 1.0]);
        }

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, videoTexture);
        gl.uniform1i(currentShaderProgram.samplerUniform, 0);

        gl.activeTexture(gl.TEXTURE1);
        gl.bindTexture(gl.TEXTURE_2D, colorizeTexture);
        gl.uniform1i(currentShaderProgram.sampler2Uniform, 1);

        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexPositionBuffer);
        gl.vertexAttribPointer(currentShaderProgram.vertexPositionAttribute,
                  biunitSquareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexTextureCoordBuffer);
        gl.vertexAttribPointer(currentShaderProgram.textureCoordAttribute,
                  biunitSquareVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);
        setMatrixUniforms();
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, biunitSquareVertexPositionBuffer.numItems);
        
        mvPopMatrix();

        gl.disable(gl.BLEND);

        gl.bindTexture(gl.TEXTURE_2D, tex0);
        gl.generateMipmap(gl.TEXTURE_2D);
        gl.bindTexture(gl.TEXTURE_2D, null);


    }


    function drawScene(framebuffer, texture, texture2) {

        gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
        drawSceneRecursive(framebuffer, texture, texture2);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);

        if (parity && mixer.invert) return;

        gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

        currentShaderProgram = copyingShaderProgram;
        gl.useProgram(currentShaderProgram);

        mat4.identity(mvMatrix);
        mvPushMatrix();

        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexPositionBuffer);
        gl.vertexAttribPointer(currentShaderProgram.vertexPositionAttribute,
                 biunitSquareVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.bindBuffer(gl.ARRAY_BUFFER, biunitSquareVertexTextureCoordBuffer);
        gl.vertexAttribPointer(currentShaderProgram.textureCoordAttribute,
                biunitSquareVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.uniform1i(currentShaderProgram.samplerUniform, 0);

        setMatrixUniforms();
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, biunitSquareVertexPositionBuffer.numItems);

        mvPopMatrix();
    }


    var lastTime = 0;
    var frameCount = 0;
    var lastFPS = 0;
    function showFPS(now) {
      var upd = 30;
      frameCount++;
      if (frameCount == 1) {
        lastFPS = now;
      } else if (frameCount == upd) {
        var elapsed = now - lastFPS;
	var fps = (upd-1) / elapsed;
        var div = document.getElementById("fps");
        if (div) div.innerHTML = Math.round(fps * 1000);
        frameCount = 0;
      }
    }

    function animate() {
        var timeNow = new Date().getTime();
	showFPS(timeNow);
        if (lastTime != 0) {
            var elapsed = timeNow - lastTime;
	    if (mixer.animate) {
              textureAngle += 0.03 * elapsed;
	    }
        }
        lastTime = timeNow;
    }

    function initFPS() {


    }
 
    parity = 0;

    var videoElement;
    var videoElement2;

    var videoFrameCount = 0;

    function updateVideo() {
        if (videoFrameCount++ % mixer.stepDown) return;
        gl.bindTexture(gl.TEXTURE_2D, videoTexture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, videoElement);
    }

    function changePause(pause) {
       if (pause) {
           videoElement.pause();
       } else {
           videoElement.play();
       }
    }
    function changeSource(url) {
     localizeMedia('yt', url, function(u2) {
       videoElement.src = u2;
     });
    }
    function changeColorize(url) {
       localizeMedia('im', url, function(u2) {
         colorize.src = u2;
       });
    }
    function changeStartTime(t) {
       videoElement.currentTime = t;
    }     

    function localizeMedia(cgi, url, callback) {
	var http = new XMLHttpRequest();
	var u = cgi + '.cgi?v=' + escape(url);
	http.open('GET', u, true);
	http.onreadystatechange = function () {
	    if (http.readyState == 4 && http.status == 200) {
		callback(http.responseText);
	    }
	};
	http.send(null);
    }

    function tick() {
	// videoElement.muted = true;
        requestAnimFrame(tick);

        if (mixer.pause) return;
	if (parity) {
          drawScene(rttFramebuffer0, rttTexture0, rttTexture1);
        } else {
          drawScene(rttFramebuffer1, rttTexture1, rttTexture0);
	}
        parity = !parity;
        animate();
	updateVideo();
    }

    var gui;
    var mixer = {
      pause: false,
      animate: true,
      invert: false,
      lumakey: 1.0,
      luma_bw: false,
      brightness: 1.0,
      contrast: 1.15,
      saturation: 1.0,
      scale: 0.89,
      stepDown: 1,
      jumpyBar: false,
      source: "http://www.youtube.com/watch?v=MP-td3C55Yc",
      colorize: false,
      palette: "http://sp0t.org/videoriot/Morris_Louis_Where.jpg",
      start_time: 0.0,
    };

    var preset0 = {invert: true,  luma_bw: false, lumakey: 0.7,  scale: 0.9, jumpyBar: false, source: 'http://www.youtube.com/watch?v=yY5NuRpQ18I', start_time: 0,  colorize: false};
    var preset1 = {invert: false, luma_bw: true,  lumakey: 0.25, scale: 0.5, jumpyBar: false, source: 'http://www.youtube.com/watch?v=MP-td3C55Yc', start_time: 6,  colorize: false};
    var preset2 = {invert: true , luma_bw: false, lumakey: 0.4,  scale: 0.9, jumpyBar: false, source: 'http://www.youtube.com/watch?v=bESGLojNYSo', start_time: 26, colorize: true, palette: "http://sp0t.org/videoriot/Morris_Louis_Where.jpg"};
    var preset3 = {invert: true,  luma_bw: true,  lumakey: 0.2,  scale: 1.1, jumpyBar: false, source: 'http://www.youtube.com/watch?v=Sr2JneittqQ', start_time: 23, colorize: true, palette: "http://www.allbestpictures.com/wallpapers/flowers/image/love_blooms_roses,_bunch_of_flowers.jpg"};
    var preset4 = {invert: true,  luma_bw: true,  lumakey: 0.4,  scale: 0.9, jumpyBar: false, source: 'http://www.youtube.com/watch?v=nHlJODYBLKs', start_time: 10, colorize: true, palette: "http://sp0t.org/videoriot/picasso-1932.jpg"};
    var preset5 = {invert: false, luma_bw: true,  lumakey: 0.15, scale: 0.5, jumpyBar: false, source: 'http://www.youtube.com/watch?v=GtiDTT8JQsY', start_time: 0,  colorize: true, palette: "http://sp0t.org/videoriot/Paul_Klee_Fire_in_the_Evening.jpg"};
    var preset6 = {invert: true,  luma_bw: false, lumakey: 0.3,  scale: 0.5, jumpyBar: false, source: 'http://www.youtube.com/watch?v=o_Jnbrcj3-g', start_time: 6,  colorize: false};
    var preset7 = {invert: true,  luma_bw: true,  lumakey: 0.3,  scale: 1.1, jumpyBar: false, source: 'http://www.youtube.com/watch?v=3xWiBCIxjIk', start_time: 6,  colorize: false};


    function deadman() {
      mixer.animate = true;
    }

    function gup(name) {
      name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
      var regexS = "[\\?&]"+name+"=([^&#]*)";
      var regex = new RegExp( regexS );
      var results = regex.exec( window.location.href );
      if( results == null )
	return "";
      else
	return results[1];
    }

    var banner_height = 30;
    var banner_margin = 7;
    function start() {
        /* if (navigator.appVersion.indexOf('Chrome') < 0) {
          document.body.innerHTML = 'requires <a href=http://www.google.com/chrome>chrome</a>';
          return;
        } */
        var banner = document.getElementById("banner");
        banner.style.height = banner_height - 2*banner_margin;
        banner.style.margin = banner_margin;

        var canvas = document.getElementById("feedback-canvas");
        videoElement = document.getElementById("video");

        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight - banner_height;

        window.onresize = function() {
          canvas.width = window.innerWidth;
          canvas.height = window.innerHeight - banner_height;
          gl.viewportWidth = canvas.width;
          gl.viewportHeight = canvas.height;
        };
    
        initGL(canvas);
        var deadman;
        canvas.onmousemove = function(e) {
          if (deadman) {
             clearTimeout(deadman);
          }
          deadman = setTimeout("deadman()", 5000);
          mixer.animate = false;
          mixer.scale = 0.4 + e.clientX / window.innerWidth;
          textureAngle = 2.0 * (e.clientY - window.innerHeight / 2);
        }



        gui = new DAT.GUI();
        gui.add(mixer, 'source').onChange(changeSource);
        gui.add(mixer, 'colorize');
        gui.add(mixer, 'palette').onChange(changeColorize);
        gui.add(mixer, 'start_time').onChange(changeStartTime);
        gui.add(mixer, 'pause').onChange(changePause);
        gui.add(mixer, 'invert');
        gui.add(mixer, 'lumakey').min(0.0).max(1.0);
        gui.add(mixer, 'luma_bw');
        gui.add(mixer, 'brightness').min(0.7).max(1.3);
        gui.add(mixer, 'contrast').min(0.7).max(1.3);
        gui.add(mixer, 'saturation').min(0.7).max(1.3);
        gui.add(mixer, 'scale').min(0.3).max(1.5);
        // gui.add(mixer, 'stepDown').min(1).max(10);
        // gui.add(mixer, 'jumpyBar');

        var preset_menu = document.getElementById("presets");
        presets = new Presets(gui, mixer, preset_menu);
        presets.add(preset1);
        presets.add(preset2);
        presets.add(preset3);
        presets.add(preset4);
        presets.add(preset5);
        presets.add(preset0);
        but1();

        for (parm in mixer) {
          var val = gup(parm);
          if (val) {
             var t = typeof(mixer[parm]);
             if ('number' == t) {
                mixer[parm] = parseFloat(val);
             } else if ('boolean' == t) {
                mixer[parm] = (val == 'true');
             } else {
                mixer[parm] = unescape(val);
             }
             if (parm == "source") {
                changeSource(mixer.source);
             }
          }
        }
        mixer.animate = true;
        initFPS();

        rttTexture0 = createTexture();
        rttFramebuffer0 = createFramebuffer(rttTexture0);

        rttTexture1 = createTexture();
        rttFramebuffer1 = createFramebuffer(rttTexture1);

        gl.bindTexture(gl.TEXTURE_2D, null);
        gl.bindRenderbuffer(gl.RENDERBUFFER, null);
        gl.bindFramebuffer(gl.FRAMEBUFFER, null);

        initShaders();
        initBuffers();
        initTextureMaps();

	// XXX don't need to do this here since its onload
        var colorizeElement = document.getElementById("colorize");
        gl.bindTexture(gl.TEXTURE_2D, colorizeTexture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, colorizeElement);


        gl.clearColor(0.0, 0.0, 0.0, 1.0);

        tick();
    }

function playing() {
   if (videoElement)
     videoElement.currentTime = mixer.start_time;
}
function colorizeload() {
        var colorizeElement = document.getElementById("colorize");
        gl.bindTexture(gl.TEXTURE_2D, colorizeTexture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, colorizeElement);
}
function but0() {
   presets.trigger(preset0);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but1() {
   presets.trigger(preset1);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but2() {
   presets.trigger(preset2);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but3() {
   presets.trigger(preset3);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but4() {
   presets.trigger(preset4);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but5() {
   presets.trigger(preset5);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but6() {
   presets.trigger(preset6);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}
function but7() {
   presets.trigger(preset7);
   changeSource(mixer.source);
   changeColorize(mixer.palette);
}

function about() {
   var dom = document.getElementById("about");
   dom.style.visibility = "visible";
}

function aboutclose() {
   var dom = document.getElementById("about");
   dom.style.visibility = "hidden";
}

function sharebut() {
   var url = window.location.href;
   var q = url.indexOf('?');
   if (q > 0) {
      url = url.substring(0, q+1);
   } else {
      url += '?';
   }
   for (parm in mixer) {
     url += parm + '=' + escape(mixer[parm]) + '&';
   }
   window.prompt("Copy this URL and send it anywhere", url);
}
</script>


</head>


<body onload="start();">


<div style="margin:8" id="banner">
Video Riot
&nbsp;
&nbsp;
<span style="cursor:pointer" onclick="about()">about/help</span>
&nbsp;
&nbsp;
<span style="cursor: pointer" onclick="but1()">London</span>
<span style="cursor: pointer" onclick="but7()">Egypt</span>
<span style="cursor: pointer" onclick="but6()">Syria</span> - 
<span style="cursor: pointer" onclick="but2()">Gaga</span>
<span style="cursor: pointer" onclick="but3()">Daft</span>
<span style="cursor: pointer" onclick="but4()">Okgo</span> -
<span style="cursor: pointer" onclick="but5()">Burn</span>
<span style="cursor: pointer" onclick="but0()">Codex</span>
&nbsp;
&nbsp;
<span style="cursor: pointer" onclick="sharebut()">share your creation</span>
</div>



  <canvas id="feedback-canvas" style="border: none;" width="500" height="200"></canvas> <br>


<video id="video" autoplay loop onplay="playing()"></video>

<div style="visibility:hidden; position:fixed; top:50; left:20; width:400; zIndex:1002; background-color:#444; padding:10;" id="about">

by <a target="_blank" href=http://scottdraves.com>Scott Draves</a><br>
thanks to <a target="_blank" href=http://chenalexander.com>Alexander Chen</a> and
<a target="_blank" href=http://www.goldbergs.com/>Josh Goldberg</a><p>

The effect is based on video feedback implemented with WebGL and its
shading language, which means most of the work is done on your GPU.
The videos are in WebM format which works on Chrome and Firefox.

<p>

Besides the presets, click to open the controls then paste in the URL
of any video from YouTube to change the source.  Then adjust the
parameters to get it looking good.  Then click on "share" and send the
URL anywhere to reproduce what you see.

<p>


Basically it combines luma key with a feedback layer transformed with
scale-rotate-translate and mirror tiling.  The scale and rotation are
connected to the mouse, but if you don't move the mouse for a while,
then they get adjusted automatically.

<p>

You can get the source code from <a href=http://code.google.com/p/videoriot/>Google Code</a>.


<p>

<button onclick="aboutclose()">ok</button>

</div>

<img id="colorize" onload="colorizeload()" src="Morris_Louis_Where.jpg">

</body>

</html>
